﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.Ccr.Core;
using NUnit.Framework;
using XcoAppSpaces.Core;

namespace test.XcoAppSpaces.Core.Test_Load_Balancer_Extension
{
    #region Load Balancer
	[Serializable]
    public class RegisterLoadCarrier<TLoad>
    {
        public Port<TLoad> LoadCarrier;
    }


    [XcoWorkerExtension]
    public class LoadBalancer<TLoad>
    {
        private readonly List<RegisterLoadCarrier<TLoad>> carriers = new List<RegisterLoadCarrier<TLoad>>();
        private int currentCarrierIndex = -1;


        [XcoConcurrent]
        void ProcessRegisterLoadCarrier(RegisterLoadCarrier<TLoad> registration)
        {
            lock (this.carriers)
            {
                Console.WriteLine("  Load Balancer: Registering load carrier");
                this.carriers.Add(registration);
            }
        }


        public void Post(TLoad task)
        {
            lock (this.carriers)
            {
                if (this.carriers.Count > 0)
                {
                    this.currentCarrierIndex = (this.currentCarrierIndex + 1) % this.carriers.Count;
                    Console.WriteLine("  Load Balancer: Next load carrier = {0}", this.currentCarrierIndex);
                    this.carriers[this.currentCarrierIndex].LoadCarrier.Post(task);
                }
            }
        }
    }
    #endregion

    class PWorkerWithLoadBalancer : PortSet<string, RegisterLoadCarrier<string>>
    {}

    class WorkerWithLoadBalancer : PWorkerWithLoadBalancer
    {
        private readonly LoadBalancer<string> lb = new LoadBalancer<string>();

        [XcoConcurrent]
        private void ProcessString(string msg)
        {
            this.lb.Post(msg);
        }
    }


    class MyStringPort : Port<string>
    {}


    [TestFixture]
    public class testLoadBalancer
    {
        [Test]
        public void testWireUp()
        {
			using (XcoAppSpace space = new XcoAppSpace("tcp.port=9010"))
            {
                PWorkerWithLoadBalancer w;
                w = space.RunWorker<PWorkerWithLoadBalancer, WorkerWithLoadBalancer>("worker");

				using (XcoAppSpace client = new XcoAppSpace("tcp.port=0"))
                {
                    PWorkerWithLoadBalancer server;
                    server = client.ConnectWorker<PWorkerWithLoadBalancer>("localhost:9010/worker");

                    // so geht es nicht:
                    MyStringPort pLoadCarrier = new MyStringPort();

                    //so geht es:
                    //Port<string> pLoadCarrier = new Port<string>();

                    AutoResetEvent signalResult = new AutoResetEvent(false);
                    string msgReceived = "";
                    Arbiter.Activate(
                        space.DefaultDispatcherQueue,
                        pLoadCarrier.Receive(text =>
                                                 {
                                                     msgReceived = text;
                                                     signalResult.Set();
                                                 })
                        );

                    Port<Exception> pEx = new Port<Exception>();
                    Arbiter.Activate(
                        space.DefaultDispatcherQueue,
                        Arbiter.Receive(
                            true,
                            pEx,
                            Console.WriteLine
                            )
                        );
                    server.PostWithCausality(new RegisterLoadCarrier<string> {LoadCarrier = pLoadCarrier}, pEx);
					Thread.Sleep(200); // give registration time to complete

                    server.Post("hello");
                    Assert.IsTrue(signalResult.WaitOne(1000));
                    Assert.AreEqual("hello", msgReceived);
                }
            }
        }
    }
}
